<?php

namespace pmvc\mvc;

use pmvc\util\StringUtil;
use pmvc\log\Log;

class Request {

	private $dispatcher;
	private $url;
	private $requestedUrl;
	private $contextUrl;
	private $baseUrl;
	private $secure = FALSE;
	private $attributes = Array();
	private $pathVariables = Array();
	private $headers = NULL;

	/**
	 * Creates a Request object for the given {@link Dispatcher}
	 * and url.
	 * @param Dispatcher $dispatcher
	 * @param string $url
	 */
	public function __construct(Dispatcher $dispatcher, $url) {
		$this->dispatcher 	= $dispatcher;
		$this->url			= $url;

		// parse url
		$this->url = (strpos($url, '?')===FALSE)
			? $url
			: substr($url, 0, strpos($url, '?'));
		Log::trace("url parsed as {0}", $this->contextUrl);

		// parse context url
		$parts = explode("/", getenv("SCRIPT_NAME"));
		$this->contextUrl = (count($parts)>0)
			? str_replace("/".$parts[count($parts)-1], "", getenv("SCRIPT_NAME"))
			: "";
		Log::trace("contextUrl parsed as {0}", $this->contextUrl);

		// parse requested url
		if ($this->contextUrl!="") {
			$pos = strpos($this->url, $this->contextUrl);
			$this->requestedUrl = ($pos!==FALSE && $pos==0)
				? substr($this->url, strlen($this->contextUrl))
				: $this->url;
		} else {
			$this->requestedUrl = $this->url;
		}
		Log::trace("requestedUrl parsed as {0}", $this->requestedUrl);

		// check to see if it's secure
		$this->secure = array_key_exists("HTTPS", $_SERVER);
		Log::trace("Is secure: {0}", $this->secure);

		// parse baseUrl
		$http = array_key_exists("HTTPS", $_SERVER) ? "https" : "http";
		if (array_key_exists("HTTP_HOST", $_SERVER)) {
			$this->baseUrl .= $http."://".$_SERVER["HTTP_HOST"];
		} else if (array_key_exists("SERVER_NAME", $_SERVER)) {
			$this->baseUrl .= $http."://".$_SERVER["SERVER_NAME"];
		} else if (array_key_exists("SERVER_ADDR", $_SERVER)) {
			$this->baseUrl .= $http."://".$_SERVER["SERVER_ADDR"];
		}
		if (strlen($this->baseUrl)>0
			&& array_key_exists("SERVER_PORT", $_SERVER)
			&& $_SERVER["SERVER_PORT"]!=80
			&& $_SERVER["SERVER_PORT"]!=443) {
				$this->baseUrl .= ":".$_SERVER["SERVER_PORT"];
		}
		$this->baseUrl .= $this->contextUrl;
		Log::trace("baseUrl parsed as {0}", $this->baseUrl);
	}

	/**
	 * Returns the {@link Dispatcher} that created this Request.
	 */
	public function getDispatcher() {
		return $this->dispatcher;
	}

	/**
	 * Checks to see if the request is a secure request.
	 * @return a boolean
	 */
	public function isSecure() {
		return $this->secure;
	}

	/**
	 * Returns the URL that was requested.
	 * @return string the URL
	 */
	public function getRequestedUrl() {
		return $this->requestedUrl;
	}

	/**
	 * Returns the context URL
	 * @return string the context URL
	 */
	public function getContextUrl() {
		return $this->contextUrl;
	}

	/**
	 * Returns the context URL
	 * @return string the context URL
	 */
	public function getBaseUrl() {
		return $this->baseUrl;
	}

	/**
	 * Returns the full url as requested
	 * by the web browser.
	 */
	public function getFullUrl($includeQueryString=TRUE) {
		$qs = $_SERVER["QUERY_STRING"];
		return $this->baseUrl
			.$this->requestedUrl
			.((!empty($qs) && $includeQueryString) ? "?".$qs : "");
	}

	/**
	 * Returns the query string.
	 */
	public function getQueryString() {
		return $_SERVER["QUERY_STRING"];
	}

	/**
	 * Returns all of the client's request headers.
	 * @return Array
	 */
	public function getHeaders() {
		if ($this->headers==NULL) {
			// use the apache module
			if (function_exists("apache_request_headers")) {
				$this->headers = apache_request_headers();
			// parse manually
			} else {
				$this->headers = Array();
				foreach ($_SERVER as $k => $v) {
					if (substr($k, 0, 5) == "HTTP_") {
						$k = str_replace('_', ' ', substr($k, 5));
						$k = str_replace(' ', '-', ucwords(strtolower($k)));
						$this->headers[$k] = $v;
					}
				}
			}
		}
		return $this->headers;
	}

	/**
	 * Returns the given header.
	 * @param string $name
	 */
	public function getHeader($name) {
		$headers = $this->getHeaders();
		$name = strtolower($name);
		foreach ($headers as $header => $val) {
			if (strtolower($header)==$name) {
				return $val;
			}
		}
		return NULL;
	}

	/**
	 * Indicates whether or not the given header was
	 * given to us by the client.
	 * @param string $name the name
	 */
	public function containsHeader($name) {
		$headers = $this->getHeaders();
		return array_key_exists(strtolower($name), $headers);
	}

	/**
	 * Returns all of the request parameters.
	 */
	public function getParameters() {
		return isset($_REQUEST) ? $_REQUEST : Array();
	}
	
	/**
	 * Returns the value of a request parameter, or the default
	 * value provided.
	 * @param string $name the name of the request param
	 * @param mixed $default the default value
	 */
	public function getParameter($name, $default=NULL) {
		// do we want an array?
		$wantsArray = StringUtil::endsWith($name, "[]");

		// automatically find array names
		$name = ($wantsArray)
			? substr($name, 0, strlen($name)-2) : $name;
		$arrayName = ($wantsArray)
			? $name : $name."[]";

		// get the value
		$val = NULL;
		if ($this->hasParameter($name)) {
			$val = $_REQUEST[$name];
		} else if ($this->hasParameter($arrayName)) {
			$val = $_REQUEST[$arrayName];
		}

		// return the default
		if ($val==NULL) {
			return $default;
		}

		// return the value
		return ($wantsArray && !is_array($val))
			? Array($val) : $val;
	}

	/**
	 * Checks to see if a request parameter exists.
	 * @param string $name
	 */
	public function hasParameter($name) {
		// do we want an array?
		$wantsArray = StringUtil::endsWith($name, "[]");

		// automatically find array names
		$name = ($wantsArray)
			? substr($name, 0, strlen($name)-2) : $name;
		$arrayName = ($wantsArray)
			? $name : $name."[]";

		// check for the parameter
		return isset($_REQUEST) && (
			array_key_exists($name, $_REQUEST) && isset($_REQUEST[$name])
			|| array_key_exists($arrayName, $_REQUEST) && isset($_REQUEST[$arrayName])
		);
	}

	/**
	 * Returns an array of all pathVariables set.
	 */
	public function getPathVariables() {
		return $this->pathVariables;
	}

	/**
	 * Returns an pathVariable by it's name or the
	 * (optional) default value if it doesn't exist.
	 * @param string $name the pathVariable's name
	 * @param mixed $default the default value
	 */
	public function getPathVariable($name, $default=NULL) {
		return ($this->hasPathVariable($name))
			? $this->pathVariables[$name] : $default;
	}

	/**
	 * Checks to see if the Request has an pathVariable.
	 * @param string $name
	 */
	public function hasPathVariable($name) {
		return array_key_exists($name, $this->pathVariables);
	}

	/**
	 * Sets an pathVariable's value by it's name.
	 * @param string $name the name of the pathVariable
	 * @param mixed $value the value
	 */
	public function setPathVariable($name, $value) {
		$this->pathVariables[$name] = $value;
	}

	/**
	 * Removes an pathVariable by it's name.
	 * @param string $name the name
	 */
	public function removePathVariable($name) {
		unset($this->pathVariables[$name]);
	}

	/**
	 * Returns an array of all attributes set.
	 */
	public function getAttributes() {
		return $this->attributes;
	}

	/**
	 * Returns an attribute by it's name or the
	 * (optional) default value if it doesn't exist.
	 * @param string $name the attribute's name
	 * @param mixed $default the default value
	 */
	public function getAttribute($name, $default=NULL) {
		return ($this->hasAttribute($name))
			? $this->attributes[$name] : $default;
	}

	/**
	 * Checks to see if the Request has an attribute.
	 * @param string $name
	 */
	public function hasAttribute($name) {
		return array_key_exists($name, $this->attributes);
	}

	/**
	 * Sets an attribute's value by it's name.
	 * @param string $name the name of the attribute
	 * @param mixed $value the value
	 */
	public function setAttribute($name, $value) {
		$this->attributes[$name] = $value;
	}

	/**
	 * Removes an attribute by it's name.
	 * @param string $name the name
	 */
	public function removeAttribute($name) {
		unset($this->attributes[$name]);
	}

	/**
	 * Returns the current user's session id.
	 */
	public function getSessionId() {
		return session_id();
	}

	/**
	 * Returns an array of all session attributes set.
	 */
	public function getSessionVariables() {
		return isset($_SESSION) ? $_SESSION : Array();
	}

	/**
	 * Returns a session attribute by it's name or the
	 * (optional) default value if it doesn't exist.
	 * @param string $name the attribute's name
	 * @param mixed $default the default value
	 */
	public function getSessionVariable($name, $default=NULL) {
		return ($this->hasSessionVariable($name))
			? $_SESSION[$name] : $default;
	}

	/**
	 * Checks to see if the session has an attribute.
	 * @param string $name
	 */
	public function hasSessionVariable($name) {
		return array_key_exists($name, $_SESSION);
	}

	/**
	 * Sets a session attribute's value by it's name.
	 * @param string $name the name of the attribute
	 * @param mixed $value the value
	 */
	public function setSessionVariable($name, $value) {
		$_SESSION[$name] = $value;
	}

	/**
	 * Removes a session attribute by it's name.
	 * @param string $name the name
	 */
	public function removeSessionVariable($name) {
		unset($_SESSION[$name]);
	}

}

?>